using System;
using System.Collections;
using System.Collections.Specialized;
using System.Windows;

namespace TrueCode.WPF
{
  /// <summary>
  /// This class is used to adapt a collection binded to a WPF element to be changed from any thread.
  /// Create an instance in code or xaml, bind Source property to the collection you want adapt, than
  /// bind target element to Collection property of this class.
  /// </summary>
  public class ThreadSafeCollectionAdapter : Freezable
  {
    #region Dependency Properties
    public static readonly DependencyProperty SourceProperty
      = DependencyProperty.Register( "Source", typeof( object ), typeof( ThreadSafeCollectionAdapter ), new PropertyMetadata( OnSourceChanged ) );

// ReSharper disable InconsistentNaming
    private static readonly DependencyPropertyKey CollectionPropertyKey
      = DependencyProperty.RegisterReadOnly( "Collection", typeof( IEnumerable ), typeof( ThreadSafeCollectionAdapter ), new PropertyMetadata( ) );
// ReSharper restore InconsistentNaming

    public static readonly DependencyProperty CollectionProperty = CollectionPropertyKey.DependencyProperty;
    #endregion


    #region Properties
    public object Source
    {
      get { return GetValue( SourceProperty ); }
      set{ SetValue( SourceProperty, value); }
    }

    public IEnumerable Collection
    {
      get { return (IEnumerable)GetValue( CollectionProperty ); }
    }
    #endregion

    #region Entry points
    protected override Freezable CreateInstanceCore()
    {
      return new ThreadSafeCollectionAdapter();
    }

    private static void OnSourceChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
    {
      if( !( e.NewValue is IList ) || !( e.NewValue is INotifyCollectionChanged ) )
        throw new ArgumentException( "Source must implement IList and INotifyCollectionChanged" );
      dependencyObject.SetValue( CollectionPropertyKey, new ThreadSafeCollection( dependencyObject.Dispatcher, e.NewValue ) );
    }
    #endregion
  }
}